package gu.sql2java.generator;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.collect.ImmutableMap;

import gu.sql2java.excel.annotations.ExcelColumn;
import gu.sql2java.excel.annotations.ExcelSheet;
import net.gdface.utils.Pair;

import static com.google.common.base.Strings.nullToEmpty;
import static net.gdface.utils.MiscellaneousUtils.elementsOf;
import static com.google.common.base.Preconditions.checkState;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * SQL 语句注释字段(COMMENT)自定义标记解析
 * @author guyadong
 *
 */
class SqlComment {
	/**
	 * 正表达式提取字段注释({@link #remarks})中的JSON字段定义
	 */
	private static final String JSON_FIELD_REGEX="^(\\[JSON_STR(?:,(array|obj(?:ect)?|[a-z][\\w\\.]+\\w+))?\\])(.*)";

	/**
	 * 正表达式提取字段注释({@link #remarks})中的字段名标记
	 */
	private static final String NAME_TAG_REGEX="X@NAME\\s*:(.+?)@X";
	/**
	 * 正表达式提取字段注释({@link #remarks})中的多符号定义标记
	 */
	private static final String NAMES_TAG_REGEX="X@NAMES\\s*:\\s*((?:\\w+[=:-].+?,?)+)@X";
	/**
	 * 正表达式提取字段注释({@link #remarks})中的多值转换定义标记
	 */
	private static final String VALUES_TAG_REGEX="X@VALUES\\s*:((?:.+?[=:-].+?,?)+)@X";

	/**
	 * 正表达式提取字段注释({@link #remarks})中的ExcelColumn注解定义标记
	 */
	private static final String COLUMN_TAG_REGEX="X@COLUMN\\s*:((?:(?:\\s*\\w+\\s*)=(?:.+?),?)+?)@X";
	/**
	 * 正表达式提取字段注释({@link #remarks})中的ExcelSheet注解定义标记
	 */
	private static final String SHEET_TAG_REGEX="X@SHEET\\s*:((?:(?:\\s*\\w+\\s*)=(?:.+?),?)+?)@X";
	/**
	 * 正表达式提取字段注释({@link #remarks})中的NUM字段定义
	 */
	private static final String NUM_FIELD_REGEX="^(\\[NUM,(Boolean|Byte|Short|Integer|Long|Float|Double)\\])(.*)";
	
	/**
	 * 正表达式提取字段注释({@link #remarks})中的ANN字段定义,用于插入Java注解代码
	 */
	private static final String ANNOT_FIELD_REGEX="ANN(@.+?)@NNA";
	
	/**
	 * 正表达式提取字段注释({@link #remarks})中的TYPE字段定义,用于插入Java注解代码
	 */
	private static final String TYPE_FIELD_REGEX="TYPE@(.+?)@EPYT";
	
	private static final Set<String> EXCEL_COLUMN_FIELDS = methodNamesOf(ExcelColumn.class);
	private static final Set<String> EXCEL_SHEET_FIELDS = methodNamesOf(ExcelSheet.class);
	private final String remarks;
	/**
	 * 注解中NAME标记字段定义的字段名,如果没有定义NAME tag则为空字符串,参见{@link #NAME_TAG_REGEX}
	 * @see #setRemarks(String)
	 */
	private final String descName;
	/**
	 * 注解中NAMES标记字段定义的多符号定义,如果没有定义NAMES tag则为空字符串,参见{@link #VALUES_TAG_REGEX}
	 * @see #setRemarks(String)
	 */
	private final String namesTag;
	/**
	 * 注解中NAMES标记字段定义的多符号定义的名字映射,如果没有定义NAMES tag则为空,参见{@link #NAMES_TAG_REGEX}
	 * @see #setRemarks(String)
	 */
	private final Map<String, String> namesTagMap;
	/**
	 * 注解中VALUES标记字段定义的多符号定义列表,如果没有定义VALUES tag则为空字符串,参见{@link #VALUES_TAG_REGEX}
	 * @see #setRemarks(String)
	 */
	private final String valuesTag;
	/**
	 * 注解中VALUES标记字段定义的多符号定义的名字映射,如果没有定义VALUES tag则为空,参见{@link #VALUES_TAG_REGEX}
	 * @see #setRemarks(String)
	 */
	private final Map<String, String> valuesTagMap;
	/**
	 * 注解中COLUMN标记字段定义的ExcelColumn字段定义列表,如果没有定义COLUMN tag则为空字符串,参见{@link #COLUMN_TAG_REGEX}
	 * @see #setRemarks(String)
	 */
	private final String columnTag;
	/**
	 * 注解中COLUMN标记字段定义的ExcelColumn字段定义映射,如果没有定义COLUMN tag则为空,参见{@link #COLUMN_TAG_REGEX}
	 * @see #setRemarks(String)
	 */
	private final Map<String, String> columnTagMap;
	/**
	 * 注解中SHEET标记字段定义的ExcelSheet字段定义列表,如果没有定义SHEET tag则为空字符串,参见{@link #SHEET_TAG_REGEX}
	 * @see #setRemarks(String)
	 */
	private final String sheetTag;
	/**
	 * 注解中SHEET标记字段定义的ExcelSheet字段定义映射,如果没有定义SHEET tag则为空,参见{@link #SHEET_TAG_REGEX}
	 * @see #setRemarks(String)
	 */
	private final Map<String, String> sheetTagMap;

	/**
	 * 删除{@code X@ @X}标记的注释字段信息
	 */
	private final String normalizedRemarks;

	SqlComment(String remarks) {
		this.remarks = nullToEmpty(remarks).replaceAll("/\\*", "SLASH*").replaceAll("\\*/", "*SLASH");
		if(!Strings.isNullOrEmpty(this.remarks)){
			{
				/** 从注解中提取NAME标记字段定义的字段名保存到 descName成员 ,如果没有定义NAME tag则descName为空字符串 */
				Matcher matcher=Pattern.compile(NAME_TAG_REGEX).matcher(this.remarks);
				this.descName = matcher.find() ? matcher.group(1) : "";
			}
			{
				ImmutableBiMap.<String,String>builder();
				/** 注解中提取NAMES标记字段定义的多符号定义保存到 namesTag成员,如果没有定义NAMES tag则namesTag为空字符串 */
				Pair<String, ImmutableBiMap<String, String>> pair = fetchMapDefine(NAMES_TAG_REGEX, ImmutableBiMap.<String,String>builder());
				this.namesTag = pair.getKey();				
				this.namesTagMap = new TreeMap<>(pair.getValue());				
			}
			{
				/** 注解中提取VALUES标记字段定义的多符号定义保存到 valuesTag成员,如果没有定义VALUES tag则valuesTag为空字符串 */
				Pair<String, ImmutableBiMap<String, String>> pair = fetchMapDefine(VALUES_TAG_REGEX, ImmutableBiMap.<String,String>builder());
				this.valuesTag = pair.getKey();
				this.valuesTagMap = new TreeMap<>(pair.getValue());
			}
			{
				/** 注解中提取COLUMN标记字段定义的多符号定义保存到 columnTag成员,如果没有定义COLUMN tag则columnTag为空字符串 */
				Pair<String, ImmutableMap<String, String>> pair = fetchMapDefine(COLUMN_TAG_REGEX, ImmutableMap.<String,String>builder());
				this.columnTag = pair.getKey();
				this.columnTagMap = new TreeMap<>(Maps.filterKeys(pair.getValue(), 
						k->EXCEL_COLUMN_FIELDS.contains(k)));
				
			}
			{
				/** 注解中提取SHEET标记字段定义的多符号定义保存到 sheetTag成员,如果没有定义SHEET tag则sheetTag为空字符串 */
				Pair<String, ImmutableMap<String, String>> pair = fetchMapDefine(SHEET_TAG_REGEX, ImmutableMap.<String,String>builder());
				this.sheetTag = pair.getKey();
				this.sheetTagMap = new TreeMap<>(Maps.filterKeys(pair.getValue(), 
						k->EXCEL_SHEET_FIELDS.contains(k)));
			}			
			this.normalizedRemarks = this.remarks
					.replaceAll(NAME_TAG_REGEX, "$1")
					.replaceAll(NAMES_TAG_REGEX, "$1")
					.replaceAll(VALUES_TAG_REGEX, "$1")
					.replaceAll(COLUMN_TAG_REGEX, "")
					.replaceAll(SHEET_TAG_REGEX, "")
					.replaceAll(JSON_FIELD_REGEX, "$3")
					.replaceAll(NUM_FIELD_REGEX, "$3")
					.replaceAll(ANNOT_FIELD_REGEX, "")
					.replaceAll(TYPE_FIELD_REGEX, "");
			
		}else{
			this.descName = "";
			this.namesTag = "";
			this.namesTagMap = ImmutableBiMap.of();
			this.valuesTag = "";
			this.valuesTagMap = ImmutableBiMap.of();
			this.columnTag = "";
			this.columnTagMap = ImmutableBiMap.of();
			this.sheetTag = "";
			this.sheetTagMap = ImmutableBiMap.of();
			this.normalizedRemarks = this.remarks;
		}
	}
	/**
	 * @return normalized comment content
	 */
	String getRemarks() {
		return normalizedRemarks;
	}
	/**
	 * 返回当前注解中NAME标记字段定义的字段名,如果没有定义NAME tag则返回空字符串,参见{@link #NAME_TAG_REGEX}
	 */
	String getDescName() {
		return descName;
	}
	/**
	 * 返回当前注解中NAMES标记字段定义的多符号定义,如果没有定义NAMES tag则返回空字符串,参见{@link #VALUES_TAG_REGEX}
	 */
	String getNamesTag() {
		return namesTag;
	}
	/**
	 * 返回注解中NAMES标记字段定义的多符号定义的名字映射,如果没有定义NAMES tag则为空
	 */
	Map<String, String> getNamesTagMap() {
		return namesTagMap;
	}
	/**
	 * 返回当前注解中VALUES标记字段定义的多符号定义,如果没有定义VALUES tag则返回空字符串,参见{@link #VALUES_TAG_REGEX}
	 */
	String getValuesTag() {
		return valuesTag;
	}
	/**
	 * 返回注解中VALUES标记字段定义的多符号定义的名字映射,如果没有定义VALUES tag则为空
	 */
	Map<String, String> getValuesTagMap() {
		return valuesTagMap;
	}
	/**
	 * @return columnTag
	 */
	String getColumnTag() {
		return columnTag;
	}
	/**
	 * @return columnTagMap
	 */
	Map<String, String> getColumnTagMap() {
		Map<String, String> m = new TreeMap<>(columnTagMap);
		/** 优先使用NAME tag定义的名字做name */
		if(!descName.isEmpty()){
			m.put("name",descName);
		}
		/** 优先使用NAME tag定义的名字做name */
		if(!valuesTag.isEmpty()){
			m.put("readConverterExp",valuesTag);
		}
		return m;
	}
	/**
	 * @return sheetTag
	 */
	String getSheetTag() {
		return sheetTag;
	}
	/**
	 * @return sheetTagMap
	 */
	Map<String, String> getSheetTagMap() {
		Map<String, String> m = new TreeMap<>(sheetTagMap);
		/** 优先使用NAME tag定义的名字做title */
		if(!descName.isEmpty()){
			m.put("title",descName);
		}
		return m;
	}
	/**
	 * 返回当前字段的注释是否有JSON tag
	 */
	boolean hasJsonTag(){
		return !Strings.isNullOrEmpty(remarks) && remarks.matches(JSON_FIELD_REGEX);
	}
	/**
	 * 返回当前字段的注释是否有NUM tag
	 */
	boolean hasNumTag(){
		return !Strings.isNullOrEmpty(remarks) && remarks.matches(NUM_FIELD_REGEX);
	}
	/**
	 * 返回当前字段的注释是否有ANNOT tag
	 */
	boolean hasAnnotTag(){
		return !Strings.isNullOrEmpty(remarks) && remarks.matches(ANNOT_FIELD_REGEX);
	}
	/**
	 * 从字段注释中根据正则表达式{@link #JSON_FIELD_REGEX}解析JSON的类型定义(fastjson)<br>
	 * 根据{@code [JSON_STR,...]}的定义返回不同的JSON类:
	 * <ul>
	 * <li>[JSON_STR,array] ---- JSONArray</li>
	 * <li>[JSON_STR,object],[JSON_STR,obj] ---- JSONObject</li>
	 * <li>[JSON_STR] ---- JSON</li>
	 * </ul>
	 * 如果不是JSON字段则返回{@code null}
	 */
	String getJsonType(){
		Matcher matcher=Pattern.compile(JSON_FIELD_REGEX).matcher(remarks);
		checkState(matcher.find(),"NOT FOUND JSON tag in comment : %s",remarks);
		String g2 = matcher.group(2);
		if("array".equals(g2)){
			return JSONArray.class.getName();
		}else if(null!= g2 && g2.matches("obj(?:ect)?")){
			return JSONObject.class.getName();
		}else if(null != g2){
			return g2;
		}
		return JSONObject.class.getName();
	}
	/**
	 * 从字段注释中根据正则表达式{@link #NUM_FIELD_REGEX}解析定义的整数类型<br>
	 * 根据{@code [NUM,...]}的定义返回不同的整数类型类:(Boolean|Byte|Short|Integer|Long|Float|Double),
	 * 如果不是NUM字段则返回{@code null}
	 */
	Class<?> getPrimitiveType(){
		Matcher matcher=Pattern.compile(NUM_FIELD_REGEX).matcher(remarks);
		if(!matcher.find()) {
			return null;
		}
		String g2 = matcher.group(2);
		switch(g2) {
		case "Boolean":
			return Boolean.class;
		case "Byte":
			return Byte.class;
		case "Short":
			return Short.class;
		case "Integer":
			return Integer.class;
		case "Long":
			return Long.class;
		case "Float":
			return Float.class;
		case "Double":
			return Double.class;
		}
		throw new IllegalStateException("UNSUPPORTED TYPE FOR " + g2);
	}
	/**
	 * 从字段注释中根据正则表达式{@link #ANNOT_FIELD_REGEX}解析ANN的注解定义列表<br>
	 * 如果没有定义ANN字段则返回空
	 */
	List<String> getAnnotations(){
		List<String> annots = Lists.newArrayList();
		Matcher matcher=Pattern.compile(ANNOT_FIELD_REGEX).matcher(remarks);
		while(matcher.find()) {
			annots.add(matcher.group(1));
		}
		return annots;
	}
	/**
	 * 从字段注释中根据正则表达式{@link #TYPE_FIELD_REGEX}解析TYPE的类型定义<br>
	 * 如果不是TYPE字段则返回{@code null}
	 */
	String getFieldType(){
		Matcher matcher=Pattern.compile(TYPE_FIELD_REGEX).matcher(remarks);
		if(matcher.find()) {
			return matcher.group(1);
		}
		return null;
	}
	/** 
	 * 注解中提取正则表达式定义的多符号定义保存到 Pair的key成员,
	 * 将符号定义的key-value映射保存到Pair的value字段
	 * 如果没有定义则key为空字符串,value为空
	 * @param builder Map builder
	 */
	@SuppressWarnings("unchecked")
	private <M extends ImmutableMap<String,String>> Pair<String, M> fetchMapDefine(String regex, M.Builder<String,String> builder){
		Matcher matcher=Pattern.compile(regex).matcher(this.remarks);
		String namesTag =  matcher.find() ? matcher.group(1) : "";
		if(!namesTag.isEmpty()){
			String[] def = namesTag.split(",");
			for (String item : def){
				String[] itemArray = item.split("[=:-]");
				if(itemArray.length == 2){
					String left = itemArray[0].trim();
					String right = itemArray[1].trim();
					if(!left.isEmpty()&& !right.isEmpty()){
						builder.put(left, right);
					}
				}
			}
		}
		M m = (M) builder.build();
		namesTag = m.toString();
		namesTag = namesTag.substring(1,namesTag.length()-1);
		return new Pair<String, M>(namesTag, m);
	}
	
	private static String formatValue(Class<?> type,String v){
		if(String.class.isAssignableFrom(type)){
			return "\"" + v + "\"";
		}else if(Class.class.isAssignableFrom(type)){
			return v.replaceAll("\\.class$", "") + ".class";
		}
		return v;
	}
	/**
     * 返回注解类的所有方法名,输入参数为{@code null}返回空集合
     * @param annotation
     */
    private static Set<String> methodNamesOf(Class<? extends Annotation> annClass) {
    	if(null != annClass){    		
    		return Sets.newLinkedHashSet(Lists.transform(Arrays.asList(annClass.getDeclaredMethods()),Method::getName));
    	}
    	return Collections.emptySet();
    }
    
	String getExcelAnnotation(Class<? extends Annotation>annotationType, Function<StringBuffer,Integer> headerColumns){
		final Map<String, String> tagMap ;
		if(ExcelColumn.class.equals(annotationType)){
			tagMap = getColumnTagMap();
		}else if(ExcelSheet.class.equals(annotationType)){
			tagMap = getSheetTagMap();
		}else {
			throw new IllegalArgumentException("@ExcelColumn or @ExcelSheet required for annotationType");
		}
		StringBuffer buffer = new StringBuffer("@"+annotationType.getSimpleName()+"(");
		final AtomicInteger fillCount = new AtomicInteger(0);
		if(null != headerColumns){
			Integer c = headerColumns.apply(buffer);
			if(null != c){
				fillCount.addAndGet(c);	
			}
		}
		/** 注释字段(方法)排序输出 */
		Ordering.natural()
		    .onResultOf(Method::getName)
		    .sortedCopy(Arrays.asList(annotationType.getDeclaredMethods()))
		    .forEach(m->{
			String k = m.getName();
			String v = tagMap.get(k);
			if(null != v){
				if(fillCount.get() > 0){
					buffer.append(",");
				}
				Class<?> type = m.getReturnType();
				if(type.isArray()){
					Class<?> componentType = type.getComponentType();
					String elements = Joiner.on(',').join(Lists.transform(elementsOf(v,"|"),s->formatValue(componentType, s)));
					buffer.append(k + "={").append(elements).append("}");
				}else{
					buffer.append(k + "=").append(formatValue(type, v));
				}
				fillCount.incrementAndGet();
			}
		});
		buffer.append(")");
		
		return fillCount.get() > 0 ? buffer.toString() : null;
	}
}
